/*
 * Copyright (c) 2019 The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/**
 * DOC: target_if_vdev_mgr_tx_ops.c
 *
 * This file provide definition for APIs registered through lmac Tx Ops
 */
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wmi_unified_api.h>
#include <wmi_unified_param.h>
#include <init_deinit_lmac.h>
#include <target_if_vdev_mgr_tx_ops.h>
#include <target_if_vdev_mgr_rx_ops.h>
#include <target_if.h>
#include <target_type.h>
#include <wlan_mlme_dbg.h>
#include <wlan_vdev_mgr_tgt_if_tx_defs.h>
#include <wlan_vdev_mgr_utils_api.h>
#include <wlan_cmn.h>

static QDF_STATUS target_if_vdev_mgr_register_event_handler(
					struct wlan_objmgr_psoc *psoc)
{
	return target_if_vdev_mgr_wmi_event_register(psoc);
}

static QDF_STATUS target_if_vdev_mgr_unregister_event_handler(
					struct wlan_objmgr_psoc *psoc)
{
	return target_if_vdev_mgr_wmi_event_unregister(psoc);
}

static QDF_STATUS target_if_vdev_mgr_rsp_timer_mod(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_response_timer *vdev_rsp,
					int mseconds)
{
	if (!vdev || !vdev_rsp) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_FAILURE;
	}

	qdf_timer_mod(&vdev_rsp->rsp_timer, mseconds);
	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_vdev_mgr_rsp_timer_stop(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_response_timer *vdev_rsp,
					uint8_t clear_bit)
{
	if (qdf_atomic_test_and_clear_bit(clear_bit, &vdev_rsp->rsp_status)) {
		/*
		 * This is triggered from timer expiry case only for
		 * which timer stop is not required
		 */
		if (vdev_rsp->timer_status != QDF_STATUS_E_TIMEOUT)
			qdf_timer_stop(&vdev_rsp->rsp_timer);

		vdev_rsp->timer_status = QDF_STATUS_SUCCESS;
		/*
		 * Releasing reference taken at the time of
		 * starting response timer
		 */
		wlan_objmgr_vdev_release_ref(vdev, WLAN_VDEV_TARGET_IF_ID);
		return QDF_STATUS_SUCCESS;
	}

	return QDF_STATUS_E_FAILURE;
}

#ifdef VDEV_ASSERT_MANAGEMENT
static void target_if_vdev_mgr_assert_mgmt(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_response_timer *vdev_rsp,
					uint8_t set_bit)
{
	target_if_vdev_mgr_rsp_timer_stop(vdev, vdev_rsp,
					  set_bit);
}
#else
static void target_if_vdev_mgr_assert_mgmt(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_response_timer *vdev_rsp,
					uint8_t set_bit)
{
	QDF_ASSERT(0);
}
#endif

static QDF_STATUS target_if_vdev_mgr_rsp_timer_start(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_response_timer *vdev_rsp,
					uint8_t set_bit)
{
	uint8_t vdev_id;
	uint8_t rsp_pos;
	struct wlan_objmgr_psoc *psoc;

	psoc = wlan_vdev_get_psoc(vdev);
	if (!psoc) {
		mlme_err("PSOC is NULL");
		return QDF_STATUS_E_INVAL;
	}

	vdev_id = wlan_vdev_get_id(vdev);
	/* it is expected to be only one command with FW at a time */
	for (rsp_pos = START_RESPONSE_BIT; rsp_pos <= RESPONSE_BIT_MAX;
	     rsp_pos++) {
		if (rsp_pos != set_bit) {
			if (qdf_atomic_test_bit(rsp_pos,
						&vdev_rsp->rsp_status)) {
				mlme_err("PSOC_%d VDEV_%d: Response bit is set %d",
					 wlan_psoc_get_id(psoc),
					 vdev_id, vdev_rsp->rsp_status);
				target_if_vdev_mgr_assert_mgmt(vdev, vdev_rsp,
							       rsp_pos);
			}
		}
	}

	if (qdf_atomic_test_and_set_bit(set_bit, &vdev_rsp->rsp_status)) {
		mlme_err("PSOC_%d VDEV_%d: Response bit is set %d",
			 wlan_psoc_get_id(psoc),
			 vdev_id, vdev_rsp->rsp_status);
		target_if_vdev_mgr_assert_mgmt(vdev, vdev_rsp,
					       set_bit);
	}

	/* reference taken for timer start, will be released with stop */
	wlan_objmgr_vdev_get_ref(vdev, WLAN_VDEV_TARGET_IF_ID);
	qdf_timer_start(&vdev_rsp->rsp_timer, vdev_rsp->expire_time);

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS target_if_vdev_mgr_rsp_timer_init(
					struct wlan_objmgr_vdev *vdev,
					qdf_timer_t *rsp_timer)
{
	if (!vdev || !rsp_timer) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	qdf_timer_init(NULL, rsp_timer,
		       target_if_vdev_mgr_rsp_timer_mgmt_cb,
		       (void *)vdev, QDF_TIMER_TYPE_WAKE_APPS);
	mlme_debug("VDEV_%d: Response timer initialized",
		   wlan_vdev_get_id(vdev));

	return QDF_STATUS_SUCCESS;
}

struct wmi_unified
*target_if_vdev_mgr_wmi_handle_get(struct wlan_objmgr_vdev *vdev)
{
	struct wlan_objmgr_pdev *pdev;
	struct wmi_unified *wmi_handle;

	pdev = wlan_vdev_get_pdev(vdev);
	if (!pdev) {
		mlme_err("PDEV is NULL");
		return NULL;
	}

	wmi_handle = get_wmi_unified_hdl_from_pdev(pdev);
	if (!wmi_handle) {
		mlme_err("wmi_handle is null");
		return NULL;
	}

	return wmi_handle;
}

static inline uint32_t
target_if_vdev_mlme_build_txbf_caps(struct wlan_objmgr_vdev *vdev)
{
	uint32_t txbf_cap;
	uint32_t subfer;
	uint32_t mubfer;
	uint32_t subfee;
	uint32_t mubfee;
	uint32_t implicit_bf;
	uint32_t sounding_dimension;
	uint32_t bfee_sts_cap;

	txbf_cap = 0;
	/*
	 * ensure to set these after mlme component is attached to objmgr
	 */
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_SUBFEE, &subfee);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_MUBFEE, &mubfee);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_SUBFER, &subfer);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_MUBFER, &mubfer);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_BFEE_STS_CAP,
			&bfee_sts_cap);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_IMLICIT_BF,
			&implicit_bf);
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_SOUNDING_DIM,
			&sounding_dimension);

	WMI_HOST_TXBF_CONF_SU_TX_BFEE_SET(txbf_cap, subfee);
	WMI_HOST_TXBF_CONF_MU_TX_BFEE_SET(txbf_cap, mubfee);
	WMI_HOST_TXBF_CONF_SU_TX_BFER_SET(txbf_cap, subfer);
	WMI_HOST_TXBF_CONF_MU_TX_BFER_SET(txbf_cap, mubfer);
	WMI_HOST_TXBF_CONF_STS_CAP_SET(txbf_cap, bfee_sts_cap);
	WMI_HOST_TXBF_CONF_IMPLICIT_BF_SET(txbf_cap, implicit_bf);
	WMI_HOST_TXBF_CONF_BF_SND_DIM_SET(txbf_cap, sounding_dimension);

	mlme_debug("VHT su bfee:%d mu bfee:%d su bfer:%d "
		   "mu bfer:%d impl bf:%d sounding dim:%d",
		   WMI_HOST_TXBF_CONF_SU_TX_BFEE_GET(txbf_cap),
		   WMI_HOST_TXBF_CONF_MU_TX_BFEE_GET(txbf_cap),
		   WMI_HOST_TXBF_CONF_SU_TX_BFER_GET(txbf_cap),
		   WMI_HOST_TXBF_CONF_MU_TX_BFER_GET(txbf_cap),
		   WMI_HOST_TXBF_CONF_IMPLICIT_BF_GET(txbf_cap),
		   WMI_HOST_TXBF_CONF_BF_SND_DIM_GET(txbf_cap));

	return txbf_cap;
}

static inline uint32_t
target_if_vdev_mlme_id_2_wmi(uint32_t cfg_id)
{
	int wmi_id;

	switch (cfg_id) {
	case WLAN_MLME_CFG_DTIM_PERIOD:
		wmi_id = wmi_vdev_param_dtim_period;
		break;
	case WLAN_MLME_CFG_SLOT_TIME:
		wmi_id = wmi_vdev_param_slot_time;
		break;
	case WLAN_MLME_CFG_PROTECTION_MODE:
		wmi_id = wmi_vdev_param_protection_mode;
		break;
	case WLAN_MLME_CFG_BEACON_INTERVAL:
		wmi_id = wmi_vdev_param_beacon_interval;
		break;
	case WLAN_MLME_CFG_LDPC:
		wmi_id = wmi_vdev_param_ldpc;
		break;
	case WLAN_MLME_CFG_NSS:
		wmi_id = wmi_vdev_param_nss;
		break;
	case WLAN_MLME_CFG_SUBFER:
	case WLAN_MLME_CFG_MUBFER:
	case WLAN_MLME_CFG_SUBFEE:
	case WLAN_MLME_CFG_MUBFEE:
	case WLAN_MLME_CFG_IMLICIT_BF:
	case WLAN_MLME_CFG_SOUNDING_DIM:
	case WLAN_MLME_CFG_TXBF_CAPS:
		wmi_id = wmi_vdev_param_txbf;
		break;
	case WLAN_MLME_CFG_HE_OPS:
		wmi_id = wmi_vdev_param_set_heop;
		break;
	case WLAN_MLME_CFG_RTS_THRESHOLD:
		wmi_id = wmi_vdev_param_rts_threshold;
		break;
	case WLAN_MLME_CFG_FRAG_THRESHOLD:
		wmi_id = wmi_vdev_param_fragmentation_threshold;
		break;
	case WLAN_MLME_CFG_DROP_UNENCRY:
		wmi_id = wmi_vdev_param_drop_unencry;
		break;
	case WLAN_MLME_CFG_TX_POWER:
		wmi_id = wmi_vdev_param_tx_power;
		break;
	case WLAN_MLME_CFG_AMPDU:
		wmi_id = wmi_vdev_param_ampdu_subframe_size_per_ac;
		break;
	case WLAN_MLME_CFG_AMSDU:
		wmi_id = wmi_vdev_param_amsdu_subframe_size_per_ac;
		break;
	case WLAN_MLME_CFG_MIN_IDLE_INACTIVE_TIME:
		wmi_id =
			wmi_vdev_param_ap_keepalive_min_idle_inactive_time_secs;
		break;
	case WLAN_MLME_CFG_MAX_IDLE_INACTIVE_TIME:
		wmi_id =
			wmi_vdev_param_ap_keepalive_max_idle_inactive_time_secs;
		break;
	case WLAN_MLME_CFG_MAX_UNRESPONSIVE_INACTIVE_TIME:
		wmi_id =
			wmi_vdev_param_ap_keepalive_max_unresponsive_time_secs;
		break;
	case WLAN_MLME_CFG_UAPSD:
		wmi_id = WMI_HOST_STA_PS_PARAM_UAPSD;
		break;
	case WLAN_MLME_CFG_BCN_TX_RATE_CODE:
		wmi_id = wmi_vdev_param_beacon_rate;
		break;
	case WLAN_MLME_CFG_TX_MGMT_RATE_CODE:
		wmi_id = wmi_vdev_param_mgmt_rate;
		break;
	case WLAN_MLME_CFG_LISTEN_INTERVAL:
		wmi_id = wmi_vdev_param_listen_interval;
		break;
	default:
		wmi_id = cfg_id;
		break;
	}

	return wmi_id;
}

static QDF_STATUS target_if_vdev_mgr_set_param_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_set_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	int param_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}
	param_id = target_if_vdev_mlme_id_2_wmi(param->param_id);
	param->param_id = param_id;
	if (param->param_id == wmi_vdev_param_txbf)
		param->param_value = target_if_vdev_mlme_build_txbf_caps(vdev);

	status = wmi_unified_vdev_set_param_send(wmi_handle, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_create_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_create_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	uint8_t vap_addr[QDF_MAC_ADDR_SIZE] = {0};

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	WLAN_ADDR_COPY(vap_addr, wlan_vdev_mlme_get_macaddr(vdev));
	status = wmi_unified_vdev_create_send(wmi_handle, vap_addr,
					      param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_start_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_start_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	struct wlan_objmgr_psoc *psoc;
	struct wlan_lmac_if_mlme_rx_ops *rx_ops;
	struct vdev_response_timer *vdev_rsp;
	uint8_t vdev_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	vdev_id = wlan_vdev_get_id(vdev);
	psoc = wlan_vdev_get_psoc(vdev);
	rx_ops = target_if_vdev_mgr_get_rx_ops(psoc);
	if (!rx_ops && !rx_ops->vdev_mgr_get_response_timer_info) {
		mlme_err("VDEV_%d: No Rx Ops", vdev_id);
		return QDF_STATUS_E_INVAL;
	}

	vdev_rsp = rx_ops->vdev_mgr_get_response_timer_info(vdev);
	if (!vdev_rsp) {
		mlme_err("VDEV_%d: Invalid response structure", vdev_id);
		return QDF_STATUS_E_FAILURE;
	}

	vdev_rsp->expire_time = START_RESPONSE_TIMER;
	if (param->is_restart)
		target_if_vdev_mgr_rsp_timer_start(vdev, vdev_rsp,
						   RESTART_RESPONSE_BIT);
	else
		target_if_vdev_mgr_rsp_timer_start(vdev, vdev_rsp,
						   START_RESPONSE_BIT);

	status = wmi_unified_vdev_start_send(wmi_handle, param);
	if (QDF_IS_STATUS_ERROR(status)) {
		vdev_rsp->timer_status = QDF_STATUS_E_CANCELED;
		vdev_rsp->expire_time = 0;
		if (param->is_restart)
			target_if_vdev_mgr_rsp_timer_stop(vdev, vdev_rsp,
							  RESTART_RESPONSE_BIT);
		else
			target_if_vdev_mgr_rsp_timer_stop(vdev, vdev_rsp,
							  START_RESPONSE_BIT);
	}

	return status;
}

static QDF_STATUS target_if_vdev_mgr_delete_response_send(
				struct wlan_objmgr_vdev *vdev,
				struct wlan_lmac_if_mlme_rx_ops *rx_ops)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev);
	struct vdev_delete_response rsp = {0};

	rsp.vdev_id = wlan_vdev_get_id(vdev);
	status = rx_ops->vdev_mgr_delete_response(psoc, &rsp);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_delete_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_delete_params *param)
{
	QDF_STATUS status;
	struct wlan_objmgr_psoc *psoc;
	struct wmi_unified *wmi_handle;
	struct vdev_response_timer *vdev_rsp;
	struct wlan_lmac_if_mlme_rx_ops *rx_ops;
	uint8_t vdev_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	vdev_id = wlan_vdev_get_id(vdev);
	psoc = wlan_vdev_get_psoc(vdev);
	rx_ops = target_if_vdev_mgr_get_rx_ops(psoc);
	if (!rx_ops && !rx_ops->vdev_mgr_get_response_timer_info) {
		mlme_err("VDEV_%d: No Rx Ops", vdev_id);
		return QDF_STATUS_E_INVAL;
	}

	vdev_rsp = rx_ops->vdev_mgr_get_response_timer_info(vdev);
	if (!vdev_rsp) {
		mlme_err("VDEV_%d: Invalid response structure", vdev_id);
		return QDF_STATUS_E_FAILURE;
	}

	vdev_rsp->expire_time = DELETE_RESPONSE_TIMER;
	target_if_vdev_mgr_rsp_timer_start(vdev, vdev_rsp,
					   DELETE_RESPONSE_BIT);

	status = wmi_unified_vdev_delete_send(wmi_handle, param->vdev_id);
	if (QDF_IS_STATUS_SUCCESS(status)) {
		/*
		 * Simulate delete response if target doesn't support
		 */
		if (!wmi_service_enabled(wmi_handle,
					 wmi_service_sync_delete_cmds) ||
		    wlan_psoc_nif_feat_cap_get(psoc,
					       WLAN_SOC_F_TESTMODE_ENABLE))
			target_if_vdev_mgr_delete_response_send(vdev, rx_ops);
	} else {
		vdev_rsp->expire_time = 0;
		vdev_rsp->timer_status = QDF_STATUS_E_CANCELED;
		target_if_vdev_mgr_rsp_timer_stop(vdev, vdev_rsp,
						  DELETE_RESPONSE_BIT);
	}

	return status;
}

static QDF_STATUS target_if_vdev_mgr_stop_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_stop_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	struct wlan_lmac_if_mlme_rx_ops *rx_ops;
	struct wlan_objmgr_psoc *psoc;
	struct vdev_response_timer *vdev_rsp;
	uint8_t vdev_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	vdev_id = wlan_vdev_get_id(vdev);
	psoc = wlan_vdev_get_psoc(vdev);
	rx_ops = target_if_vdev_mgr_get_rx_ops(psoc);

	if (!rx_ops && !rx_ops->vdev_mgr_get_response_timer_info) {
		mlme_err("VDEV_%d: No Rx Ops", vdev_id);
		return QDF_STATUS_E_INVAL;
	}

	vdev_rsp = rx_ops->vdev_mgr_get_response_timer_info(vdev);
	if (!vdev_rsp) {
		mlme_err("VDEV_%d: Invalid response structure", vdev_id);
		return QDF_STATUS_E_FAILURE;
	}

	vdev_rsp->expire_time = STOP_RESPONSE_TIMER;
	target_if_vdev_mgr_rsp_timer_start(vdev, vdev_rsp, STOP_RESPONSE_BIT);

	status = wmi_unified_vdev_stop_send(wmi_handle, param->vdev_id);
	if (QDF_IS_STATUS_ERROR(status)) {
		vdev_rsp->expire_time = 0;
		vdev_rsp->timer_status = QDF_STATUS_E_CANCELED;
		target_if_vdev_mgr_rsp_timer_stop(vdev, vdev_rsp,
						  STOP_RESPONSE_BIT);
	}

	return status;
}

static QDF_STATUS target_if_vdev_mgr_down_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_down_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_vdev_down_send(wmi_handle, param->vdev_id);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_up_send(
					struct wlan_objmgr_vdev *vdev,
					struct vdev_up_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	struct vdev_set_params sparam = {0};
	uint8_t bssid[QDF_MAC_ADDR_SIZE];
	uint8_t vdev_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	vdev_id = wlan_vdev_get_id(vdev);
	sparam.vdev_id = vdev_id;

	sparam.param_id = WLAN_MLME_CFG_BEACON_INTERVAL;
	wlan_util_vdev_get_param(vdev, WLAN_MLME_CFG_BEACON_INTERVAL,
				 &sparam.param_value);
	status = target_if_vdev_mgr_set_param_send(vdev, &sparam);
	if (QDF_IS_STATUS_ERROR(status))
		mlme_err("VDEV_%d: Failed to set beacon interval!", vdev_id);

	ucfg_wlan_vdev_mgr_get_param_bssid(vdev, bssid);

	status = wmi_unified_vdev_up_send(wmi_handle, bssid, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_beacon_tmpl_send(
					struct wlan_objmgr_vdev *vdev,
					struct beacon_tmpl_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_beacon_tmpl_send_cmd(wmi_handle, param);
	return status;
}

static QDF_STATUS target_if_vdev_mgr_set_nac_rssi_send(
				struct wlan_objmgr_vdev *vdev,
				struct vdev_scan_nac_rssi_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_vdev_set_nac_rssi_send(wmi_handle, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_set_neighbour_rx_cmd_send(
					struct wlan_objmgr_vdev *vdev,
					struct set_neighbour_rx_params *param,
					uint8_t *mac)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_vdev_set_neighbour_rx_cmd_send(wmi_handle,
							    mac, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_sifs_trigger_send(
					struct wlan_objmgr_vdev *vdev,
					struct sifs_trigger_param *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_sifs_trigger_send(wmi_handle, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_set_custom_aggr_size_cmd_send(
				struct wlan_objmgr_vdev *vdev,
				struct set_custom_aggr_size_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_vdev_set_custom_aggr_size_cmd_send(wmi_handle,
								param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_config_ratemask_cmd_send(
					struct wlan_objmgr_vdev *vdev,
					struct config_ratemask_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_vdev_config_ratemask_cmd_send(wmi_handle,
							   param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_peer_flush_tids_send(
					struct wlan_objmgr_vdev *vdev,
					struct peer_flush_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_peer_flush_tids_send(wmi_handle, param->peer_mac,
						  param);

	return status;
}

static int32_t target_if_vdev_mgr_multi_vdev_restart_get_ref(
			struct wlan_objmgr_pdev *pdev,
			struct multiple_vdev_restart_params *param,
			struct wlan_objmgr_vdev **vdev_list,
			bool *vdev_timer_started)
{
	struct wlan_objmgr_psoc *psoc;
	struct wlan_objmgr_vdev *tvdev;
	struct vdev_response_timer *vdev_rsp = NULL;
	struct wlan_lmac_if_mlme_rx_ops *rx_ops;
	uint32_t vdev_idx;
	int32_t last_vdev_idx = -1;

	psoc = wlan_pdev_get_psoc(pdev);
	rx_ops = target_if_vdev_mgr_get_rx_ops(psoc);
	if (!rx_ops && !rx_ops->vdev_mgr_get_response_timer_info) {
		mlme_err("VDEV_%d: No Rx Ops", vdev_idx);
		return last_vdev_idx;
	}

	for (vdev_idx = 0; vdev_idx < param->num_vdevs ; vdev_idx++) {
		vdev_list[vdev_idx] = wlan_objmgr_get_vdev_by_id_from_pdev(
						pdev,
						param->vdev_ids[vdev_idx],
						WLAN_VDEV_TARGET_IF_ID);
		tvdev = vdev_list[vdev_idx];
		if (!tvdev) {
			mlme_err("VDEV_%d is NULL", vdev_idx);
			return last_vdev_idx;
		}

		last_vdev_idx = vdev_idx;
		vdev_rsp =
			rx_ops->vdev_mgr_get_response_timer_info(tvdev);
		if (!vdev_rsp) {
			mlme_err("VDEV_%d: No Rx Ops", vdev_idx);
			return last_vdev_idx;
		}

		target_if_vdev_mgr_rsp_timer_start(tvdev, vdev_rsp,
						   RESTART_RESPONSE_BIT);
		vdev_timer_started[vdev_idx] = true;
	}

	return last_vdev_idx;
}

static void target_if_vdev_mgr_multi_vdev_restart_rel_ref(
				struct wlan_objmgr_pdev *pdev,
				struct wlan_objmgr_vdev **vdev_list,
				bool *vdev_timer_started,
				int32_t last_vdev_idx,
				QDF_STATUS status)
{
	struct wlan_objmgr_psoc *psoc;
	struct wlan_objmgr_vdev *tvdev;
	struct wlan_lmac_if_mlme_rx_ops *rx_ops;
	struct vdev_response_timer *vdev_rsp = NULL;
	uint32_t vdev_idx;

	psoc = wlan_pdev_get_psoc(pdev);
	rx_ops = target_if_vdev_mgr_get_rx_ops(psoc);
	for (vdev_idx = 0; vdev_idx <= last_vdev_idx; vdev_idx++) {
		tvdev = vdev_list[vdev_idx];
		if (QDF_IS_STATUS_ERROR(status)) {
			vdev_rsp =
				rx_ops->vdev_mgr_get_response_timer_info(tvdev);
			if (vdev_rsp && vdev_timer_started[vdev_idx]) {
				target_if_vdev_mgr_rsp_timer_stop(
							tvdev, vdev_rsp,
							RESTART_RESPONSE_BIT);
				vdev_timer_started[vdev_idx] = false;
			}
		}
		wlan_objmgr_vdev_release_ref(tvdev,
					     WLAN_VDEV_TARGET_IF_ID);
	}
}

static QDF_STATUS target_if_vdev_mgr_multiple_vdev_restart_req_cmd(
				struct wlan_objmgr_pdev *pdev,
				struct multiple_vdev_restart_params *param)
{
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	struct wmi_unified *wmi_handle;
	struct wlan_objmgr_psoc *psoc;
	struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_PDEV_MAX_VDEVS] = {NULL};
	bool vdev_timer_started[WLAN_UMAC_PDEV_MAX_VDEVS] = {false};
	int32_t last_vdev_idx = -1;

	if (!pdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	psoc = wlan_pdev_get_psoc(pdev);
	if (!psoc) {
		mlme_err("PSOC is NULL");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = get_wmi_unified_hdl_from_pdev(pdev);
	if (!wmi_handle) {
		mlme_err("PDEV WMI Handle is NULL!");
		return QDF_STATUS_E_INVAL;
	}

	last_vdev_idx = target_if_vdev_mgr_multi_vdev_restart_get_ref(
							pdev, param,
							vdev_list,
							vdev_timer_started);
	if (last_vdev_idx < 0 || (last_vdev_idx != (param->num_vdevs - 1))) {
		target_if_vdev_mgr_multi_vdev_restart_rel_ref(
						pdev, vdev_list,
						vdev_timer_started,
						last_vdev_idx,
						QDF_STATUS_E_FAILURE);
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_send_multiple_vdev_restart_req_cmd(wmi_handle,
								param);

	target_if_vdev_mgr_multi_vdev_restart_rel_ref(
						pdev, vdev_list,
						vdev_timer_started,
						last_vdev_idx, status);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_beacon_send(
					struct wlan_objmgr_vdev *vdev,
					struct beacon_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	status = wmi_unified_beacon_send_cmd(wmi_handle, param);

	return status;
}

static QDF_STATUS target_if_vdev_mgr_sta_ps_param_send(
					struct wlan_objmgr_vdev *vdev,
					struct sta_ps_params *param)
{
	QDF_STATUS status;
	struct wmi_unified *wmi_handle;
	int param_id;

	if (!vdev || !param) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	wmi_handle = target_if_vdev_mgr_wmi_handle_get(vdev);
	if (!wmi_handle) {
		mlme_err("Failed to get WMI handle!");
		return QDF_STATUS_E_INVAL;
	}

	param_id = target_if_vdev_mlme_id_2_wmi(param->param_id);
	param->param_id = param_id;

	status = wmi_unified_sta_ps_cmd_send(wmi_handle, param);

	return status;
}

QDF_STATUS
target_if_vdev_mgr_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops)
{
	struct wlan_lmac_if_mlme_tx_ops *mlme_tx_ops;

	if (!tx_ops) {
		mlme_err("Invalid input");
		return QDF_STATUS_E_INVAL;
	}

	mlme_tx_ops = &tx_ops->mops;
	if (!mlme_tx_ops) {
		mlme_err("No Tx Ops");
		return QDF_STATUS_E_FAILURE;
	}

	mlme_tx_ops->vdev_mlme_attach =
			target_if_vdev_mgr_register_event_handler;
	mlme_tx_ops->vdev_mlme_detach =
			target_if_vdev_mgr_unregister_event_handler;
	mlme_tx_ops->vdev_create_send = target_if_vdev_mgr_create_send;
	mlme_tx_ops->vdev_start_send = target_if_vdev_mgr_start_send;
	mlme_tx_ops->vdev_up_send = target_if_vdev_mgr_up_send;
	mlme_tx_ops->vdev_delete_send = target_if_vdev_mgr_delete_send;
	mlme_tx_ops->vdev_stop_send = target_if_vdev_mgr_stop_send;
	mlme_tx_ops->vdev_down_send = target_if_vdev_mgr_down_send;
	mlme_tx_ops->vdev_set_nac_rssi_send =
			target_if_vdev_mgr_set_nac_rssi_send;
	mlme_tx_ops->vdev_set_neighbour_rx_cmd_send =
			target_if_vdev_mgr_set_neighbour_rx_cmd_send;
	mlme_tx_ops->vdev_sifs_trigger_send =
			target_if_vdev_mgr_sifs_trigger_send;
	mlme_tx_ops->vdev_set_custom_aggr_size_cmd_send =
			target_if_vdev_mgr_set_custom_aggr_size_cmd_send;
	mlme_tx_ops->vdev_config_ratemask_cmd_send =
			target_if_vdev_mgr_config_ratemask_cmd_send;
	mlme_tx_ops->peer_flush_tids_send =
			target_if_vdev_mgr_peer_flush_tids_send;
	mlme_tx_ops->multiple_vdev_restart_req_cmd =
			target_if_vdev_mgr_multiple_vdev_restart_req_cmd;
	mlme_tx_ops->beacon_cmd_send = target_if_vdev_mgr_beacon_send;
	mlme_tx_ops->beacon_tmpl_send = target_if_vdev_mgr_beacon_tmpl_send;
	mlme_tx_ops->vdev_set_param_send =
			target_if_vdev_mgr_set_param_send;
	mlme_tx_ops->vdev_sta_ps_param_send =
			target_if_vdev_mgr_sta_ps_param_send;
	mlme_tx_ops->vdev_mgr_rsp_timer_init =
			target_if_vdev_mgr_rsp_timer_init;
	mlme_tx_ops->vdev_mgr_rsp_timer_mod =
			target_if_vdev_mgr_rsp_timer_mod;
	mlme_tx_ops->vdev_mgr_rsp_timer_stop =
			target_if_vdev_mgr_rsp_timer_stop;

	return QDF_STATUS_SUCCESS;
}
